
package scanner

import "unicode"

// Scanner contains array of characters and current position
type Scanner struct {
	text []rune
	pos int
}

func New(txt string) *Scanner {
	return &Scanner{[]rune(txt+";"), 0}
}

// Constants for tokens
const (
	None byte = iota
	Number  // Digit{Digit}
  Ident   // Letter{Letter}
	Add     // '+'
	Sub     // '-'
	Mul     // '*'
	Div     // '/'
	LPar    // '('
	RPar    // ')'
	Eos     // ';'
)

// Helper method to scan characters with given predicate
func (s* Scanner) scanWith(predicate func(r rune) bool) string {
    k := s.pos
    for predicate(s.text[s.pos]) { s.pos++ }
    return string(s.text[k:s.pos])
}

// Scan new lexeme and return it with token
func (s *Scanner) Next() (string, byte) {
  // skip whitespaces
  s.scanWith(unicode.IsSpace)

  ch := s.text[s.pos]
  // Scan numbers
  if unicode.IsDigit(ch) {
    return s.scanWith(unicode.IsDigit), Number
  }
  
  // identifiers
  if unicode.IsLetter(ch) {
    return s.scanWith(unicode.IsLetter), Ident
  }
  
  // scan symbols
  var res byte = None
  s.pos++
  switch ch {
      case '+': res = Add
      case '-': res = Sub
      case '*': res = Mul
      case '/': res = Div
      case '(': res = LPar
      case ')': res = RPar
      case ';': res = Eos
  }
    
	return string(ch), res
}

